<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Express路由使用指南：模块化拆分与虚拟路由</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
            color: #333;
            min-height: 100vh;
            padding: 20px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 20px;
        }
        
        header {
            grid-column: 1 / -1;
            text-align: center;
            padding: 30px;
            background: rgba(255, 255, 255, 0.95);
            border-radius: 15px;
            margin-bottom: 20px;
            box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15);
        }
        
        h1 {
            color: #2c3e50;
            margin-bottom: 15px;
            font-size: 2.5rem;
        }
        
        .description {
            color: #7f8c8d;
            max-width: 800px;
            margin: 0 auto;
            line-height: 1.6;
            font-size: 1.1rem;
        }
        
        .card {
            background: rgba(255, 255, 255, 0.95);
            border-radius: 12px;
            padding: 25px;
            box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
            display: flex;
            flex-direction: column;
        }
        
        .card-title {
            font-size: 1.5rem;
            color: #3498db;
            margin-bottom: 20px;
            display: flex;
            align-items: center;
            gap: 10px;
            border-bottom: 2px solid #3498db;
            padding-bottom: 10px;
        }
        
        .full-width {
            grid-column: 1 / -1;
        }
        
        .content {
            line-height: 1.6;
            margin-bottom: 15px;
        }
        
        .code-block {
            background: #2c3e50;
            color: #ecf0f1;
            padding: 20px;
            border-radius: 8px;
            font-family: 'Courier New', Courier, monospace;
            overflow-x: auto;
            margin: 15px 0;
            line-height: 1.5;
            font-size: 15px;
        }
        
        .code-comment {
            color: #7f8c8d;
        }
        
        .keyword {
            color: #e74c3c;
        }
        
        .string {
            color: #2ecc71;
        }
        
        .function {
            color: #f39c12;
        }
        
        .number {
            color: #9b59b6;
        }
        
        .directory-structure {
            background: #34495e;
            color: white;
            padding: 20px;
            border-radius: 8px;
            font-family: monospace;
            line-height: 2;
            margin: 15px 0;
        }
        
        .folder {
            color: #3498db;
        }
        
        .file {
            color: #2ecc71;
        }
        
        .tabs {
            display: flex;
            gap: 5px;
            margin-bottom: 15px;
        }
        
        .tab {
            padding: 10px 20px;
            background: #e0e0e0;
            border-radius: 5px 5px 0 0;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        
        .tab.active {
            background: #3498db;
            color: white;
        }
        
        .tab-content {
            display: none;
        }
        
        .tab-content.active {
            display: block;
        }
        
        .note {
            background: #e8f4fc;
            border-left: 4px solid #3498db;
            padding: 15px;
            border-radius: 4px;
            margin: 15px 0;
        }
        
        .note-title {
            font-weight: 600;
            color: #3498db;
            margin-bottom: 8px;
        }
        
        .route-demo {
            background: #f8f9fa;
            padding: 15px;
            border-radius: 8px;
            margin: 15px 0;
        }
        
        .route-item {
            padding: 10px;
            border-bottom: 1px solid #ddd;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        
        .route-method {
            padding: 5px 10px;
            border-radius: 3px;
            color: white;
            font-weight: bold;
            font-size: 0.8rem;
        }
        
        .get {
            background: #2ecc71;
        }
        
        .post {
            background: #3498db;
        }
        
        .put {
            background: #f39c12;
        }
        
        .delete {
            background: #e74c3c;
        }
        
        .route-path {
            font-family: monospace;
        }
        
        .interactive-demo {
            background: #2c3e50;
            padding: 20px;
            border-radius: 8px;
            color: white;
            margin: 20px 0;
        }
        
        .demo-controls {
            display: flex;
            gap: 10px;
            margin-bottom: 15px;
        }
        
        .demo-btn {
            padding: 8px 15px;
            background: #3498db;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            transition: background 0.3s;
        }
        
        .demo-btn:hover {
            background: #2980b9;
        }
        
        .demo-output {
            background: #34495e;
            padding: 15px;
            border-radius: 4px;
            min-height: 100px;
            font-family: monospace;
            white-space: pre-wrap;
        }
        
        @media (max-width: 900px) {
            .container {
                grid-template-columns: 1fr;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1><i class="fas fa-route"></i> Express路由使用指南</h1>
            <p class="description">
                本指南将介绍Express.js中路由的基本使用方法、模块化拆分技巧以及虚拟路由的实现方式，
                帮助您构建更可维护和可扩展的Node.js应用程序。
            </p>
        </header>
        
        <div class="card">
            <h2 class="card-title"><i class="fas fa-basics"></i> Express路由基础</h2>
            <div class="content">
                <p>Express.js是一个基于Node.js的Web应用框架，提供了一种简单而强大的方式来处理HTTP请求和路由。</p>
                
                <p>基本路由结构：</p>
                <div class="code-block">
                    <span class="keyword">const</span> express = <span class="function">require</span>(<span class="string">'express'</span>);
                    <span class="keyword">const</span> app = <span class="function">express</span>();
                    
                    <span class="code-comment">// 基本路由示例</span>
                    app.<span class="function">get</span>(<span class="string">'/'</span>, <span class="keyword">function</span>(req, res) {
                      res.<span class="function">send</span>(<span class="string">'Hello World!'</span>);
                    });
                    
                    app.<span class="function">post</span>(<span class="string">'/api/users'</span>, <span class="keyword">function</span>(req, res) {
                      <span class="code-comment">// 处理POST请求</span>
                    });
                    
                    app.<span class="function">listen</span>(<span class="number">3000</span>);
                </div>
            </div>
        </div>
        
        <div class="card">
            <h2 class="card-title"><i class="fas fa-sitemap"></i> 为什么需要模块化路由？</h2>
            <div class="content">
                <p>随着应用规模的增长，将所有路由放在一个文件中会变得难以维护。模块化路由提供了以下优势：</p>
                <ul style="margin-left: 20px; margin-bottom: 15px;">
                    <li>更好的代码组织</li>
                    <li>更易维护和测试</li>
                    <li>支持团队协作开发</li>
                    <li>便于重用和扩展</li>
                </ul>
                
                <div class="note">
                    <div class="note-title">最佳实践</div>
                    <p>将相关路由分组到不同的模块中，例如用户相关路由、产品相关路由等。</p>
                </div>
            </div>
        </div>
        
        <div class="card full-width">
            <h2 class="card-title"><i class="fas fa-puzzle-piece"></i> 路由模块化拆分</h2>
            
            <div class="tabs">
                <div class="tab active" data-tab="structure">目录结构</div>
                <div class="tab" data-tab="main">主应用文件</div>
                <div class="tab" data-tab="user">用户路由模块</div>
                <div class="tab" data-tab="product">产品路由模块</div>
            </div>
            
            <div class="tab-content active" id="structure">
                <p>典型的模块化路由目录结构：</p>
                <div class="directory-structure">
                    <div class="folder">my-app/</div>
                    <div class="file">├── app.js                 <span class="code-comment"># 主应用文件</span></div>
                    <div class="file">├── package.json</div>
                    <div class="folder">├── routes/               <span class="code-comment"># 路由模块目录</span></div>
                    <div class="file">│   ├── index.js          <span class="code-comment"># 路由聚合文件</span></div>
                    <div class="file">│   ├── users.js          <span class="code-comment"># 用户相关路由</span></div>
                    <div class="file">│   ├── products.js       <span class="code-comment"># 产品相关路由</span></div>
                    <div class="file">│   └── orders.js         <span class="code-comment"># 订单相关路由</span></div>
                    <div class="folder">└── controllers/          <span class="code-comment"># 控制器目录（可选）</span></div>
                </div>
            </div>
            
            <div class="tab-content" id="main">
                <p>主应用文件 (app.js)：</p>
                <div class="code-block">
                    <span class="keyword">const</span> express = <span class="function">require</span>(<span class="string">'express'</span>);
                    <span class="keyword">const</span> app = <span class="function">express</span>();
                    <span class="keyword">const</span> routes = <span class="function">require</span>(<span class="string">'./routes'</span>);
                    
                    <span class="code-comment">// 中间件设置</span>
                    app.<span class="function">use</span>(express.<span class="function">json</span>());
                    
                    <span class="code-comment">// 使用路由</span>
                    app.<span class="function">use</span>(<span class="string">'/'</span>, routes);
                    
                    <span class="function">app.listen</span>(<span class="number">3000</span>, () => {
                      console.<span class="function">log</span>(<span class="string">'服务器运行在端口3000'</span>);
                    });
                </div>
            </div>
            
            <div class="tab-content" id="user">
                <p>用户路由模块 (routes/users.js)：</p>
                <div class="code-block">
                    <span class="keyword">const</span> express = <span class="function">require</span>(<span class="string">'express'</span>);
                    <span class="keyword">const</span> router = express.<span class="function">Router</span>();
                    
                    <span class="code-comment">// 获取用户列表</span>
                    router.<span class="function">get</span>(<span class="string">'/'</span>, (req, res) => {
                      res.<span class="function">json</span>([{ id: <span class="number">1</span>, name: <span class="string">'张三'</span> }, { id: <span class="number">2</span>, name: <span class="string">'李四'</span> }]);
                    });
                    
                    <span class="code-comment">// 获取单个用户</span>
                    router.<span class="function">get</span>(<span class="string">'/:id'</span>, (req, res) => {
                      res.<span class="function">json</span>({ id: req.params.id, name: <span class="string">'王五'</span> });
                    });
                    
                    <span class="code-comment">// 创建新用户</span>
                    router.<span class="function">post</span>(<span class="string">'/'</span>, (req, res) => {
                      <span class="code-comment">// 处理创建用户逻辑</span>
                      res.<span class="function">status</span>(<span class="number">201</span>).<span class="function">json</span>({ message: <span class="string">'用户创建成功'</span> });
                    });
                    
                    module.exports = router;
                </div>
            </div>
            
            <div class="tab-content" id="product">
                <p>产品路由模块 (routes/products.js)：</p>
                <div class="code-block">
                    <span class="keyword">const</span> express = <span class="function">require</span>(<span class="string">'express'</span>);
                    <span class="keyword">const</span> router = express.<span class="function">Router</span>();
                    
                    <span class="code-comment">// 虚拟路由示例 - 产品分类</span>
                    router.<span class="function">get</span>(<span class="string">'/category/:category'</span>, (req, res) => {
                      <span class="keyword">const</span> { category } = req.params;
                      <span class="code-comment">// 根据分类获取产品逻辑</span>
                      res.<span class="function">json</span>({ 
                        category: category, 
                        products: [
                          { id: <span class="number">1</span>, name: <span class="string">`产品1 - <span class="keyword">${category}</span>`</span> },
                          { id: <span class="number">2</span>, name: <span class="string">`产品2 - <span class="keyword">${category}</span>`</span> }
                        ] 
                      });
                    });
                    
                    <span class="code-comment">// 嵌套路由示例 - 产品评论</span>
                    router.<span class="function">get</span>(<span class="string">'/:productId/reviews'</span>, (req, res) => {
                      res.<span class="function">json</span>({
                        productId: req.params.productId,
                        reviews: [
                          { id: <span class="number">1</span>, text: <span class="string">'很好用！'</span> },
                          { id: <span class="number">2</span>, text: <span class="string">'质量不错'</span> }
                        ]
                      });
                    });
                    
                    module.exports = router;
                </div>
            </div>
        </div>
        
        <div class="card full-width">
            <h2 class="card-title"><i class="fas fa-ghost"></i> 虚拟路由的使用</h2>
            
            <div class="content">
                <p>虚拟路由指的是那些不直接对应物理文件或数据库实体的路由，常用于：</p>
                <ul style="margin-left: 20px; margin-bottom: 15px;">
                    <li>API版本控制 (如 /api/v1/, /api/v2/)</li>
                    <li>多租户应用</li>
                    <li>内容过滤和分类</li>
                    <li>临时或动态资源</li>
                </ul>
                
                <p>虚拟路由示例：</p>
                <div class="code-block">
                    <span class="code-comment">// 在 routes/products.js 中</span>
                    
                    <span class="code-comment">// 虚拟分类路由</span>
                    router.<span class="function">get</span>(<span class="string">'/category/:category'</span>, (req, res) => {
                      <span class="keyword">const</span> { category } = req.params;
                      <span class="code-comment">// 这里可以是从数据库查询，也可以是虚拟数据</span>
                      <span class="keyword">const</span> virtualCategories = {
                        <span class="string">'electronics'</span>: <span class="string">'电子产品'</span>,
                        <span class="string">'books'</span>: <span class="string">'图书'</span>,
                        <span class="string">'clothing'</span>: <span class="string">'服装'</span>
                      };
                      
                      <span class="keyword">const</span> categoryName = virtualCategories[category] || category;
                      
                      res.<span class="function">json</span>({
                        category: categoryName,
                        message: <span class="string">'这是一个虚拟分类'</span>,
                        products: [
                          { id: <span class="number">1</span>, name: <span class="string">`虚拟产品1 - <span class="keyword">${categoryName}</span>`</span> },
                          { id: <span class="number">2</span>, name: <span class="string">`虚拟产品2 - <span class="keyword">${categoryName}</span>`</span> }
                        ]
                      });
                    });
                    
                    <span class="code-comment">// API版本控制示例</span>
                    router.<span class="function">get</span>(<span class="string">'/v:version/:productId'</span>, (req, res) => {
                      <span class="keyword">const</span> { version, productId } = req.params;
                      
                      <span class="code-comment">// 根据版本号返回不同格式的数据</span>
                      <span class="keyword">if</span> (version === <span class="string">'1'</span>) {
                        res.<span class="function">json</span>({ id: productId, name: <span class="string">'产品名称'</span> });
                      } <span class="keyword">else if</span> (version === <span class="string">'2'</span>) {
                        res.<span class="function">json</span>({ 
                          data: { 
                            id: productId, 
                            name: <span class="string">'产品名称'</span>,
                            description: <span class="string">'产品描述'</span> 
                          },
                          meta: { version: <span class="string">'v2'</span> }
                        });
                      } <span class="keyword">else</span> {
                        res.<span class="function">status</span>(<span class="number">400</span>).<span class="function">json</span>({ error: <span class="string">'不支持的API版本'</span> });
                      }
                    });
                </div>
            </div>
        </div>
        
        <div class="card full-width">
            <h2 class="card-title"><i class="fas fa-laptop-code"></i> 交互式路由演示</h2>
            
            <div class="interactive-demo">
                <div class="demo-controls">
                    <button class="demo-btn" data-route="/api/users">获取用户列表</button>
                    <button class="demo-btn" data-route="/api/users/1">获取用户详情</button>
                    <button class="demo-btn" data-route="/api/products/category/electronics">获取电子产品</button>
                    <button class="demo-btn" data-route="/api/products/v2/123">获取产品(V2 API)</button>
                </div>
                
                <div class="demo-output">点击上方按钮模拟API请求...</div>
            </div>
            
            <div class="content">
                <p>上面的演示展示了模块化路由和虚拟路由的实际应用。在真实环境中，这些路由会被组织在不同的模块文件中。</p>
                
                <div class="note">
                    <div class="note-title">路由聚合示例</div>
                    <p>在routes/index.js中聚合所有路由：</p>
                    <div class="code-block">
                        <span class="keyword">const</span> express = <span class="function">require</span>(<span class="string">'express'</span>);
                        <span class="keyword">const</span> router = express.<span class="function">Router</span>();
                        
                        <span class="code-comment">// 引入各个路由模块</span>
                        <span class="keyword">const</span> usersRouter = <span class="function">require</span>(<span class="string">'./users'</span>);
                        <span class="keyword">const</span> productsRouter = <span class="function">require</span>(<span class="string">'./products'</span>);
                        <span class="keyword">const</span> ordersRouter = <span class="function">require</span>(<span class="string">'./orders'</span>);
                        
                        <span class="code-comment">// 设置路由前缀</span>
                        router.<span class="function">use</span>(<span class="string">'/api/users'</span>, usersRouter);
                        router.<span class="function">use</span>(<span class="string">'/api/products'</span>, productsRouter);
                        router.<span class="function">use</span>(<span class="string">'/api/orders'</span>, ordersRouter);
                        
                        module.exports = router;
                    </div>
                </div>
            </div>
        </div>
        
        <div class="card">
            <h2 class="card-title"><i class="fas fa-lightbulb"></i> 最佳实践</h2>
            <div class="content">
                <ul style="margin-left: 20px;">
                    <li>使用Router()实例创建模块化路由</li>
                    <li>按功能或资源类型组织路由</li>
                    <li>使用中间件处理通用逻辑</li>
                    <li>为API添加版本控制</li>
                    <li>使用参数验证库</li>
                    <li>实现适当的错误处理</li>
                </ul>
            </div>
        </div>
        
        <div class="card">
            <h2 class="card-title"><i class="fas fa-exclamation-circle"></i> 常见问题</h2>
            <div class="content">
                <ul style="margin-left: 20px;">
                    <li>路由顺序问题 - 更具体的路由应放在前面</li>
                    <li>中间件使用不当导致路由无法正确响应</li>
                    <li>忘记导出路由模块</li>
                    <li>路径参数验证不充分</li>
                </ul>
            </div>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            // 标签切换功能
            const tabs = document.querySelectorAll('.tab');
            tabs.forEach(tab => {
                tab.addEventListener('click', () => {
                    // 移除所有active类
                    document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
                    document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
                    
                    // 添加active类到当前标签和对应内容
                    tab.classList.add('active');
                    const tabId = tab.getAttribute('data-tab');
                    document.getElementById(tabId).classList.add('active');
                });
            });
            
            // 交互演示功能
            const demoButtons = document.querySelectorAll('.demo-btn');
            const demoOutput = document.querySelector('.demo-output');
            
            // 模拟API响应数据
            const mockResponses = {
                '/api/users': {
                    status: 200,
                    data: [{ id: 1, name: '张三' }, { id: 2, name: '李四' }]
                },
                '/api/users/1': {
                    status: 200,
                    data: { id: 1, name: '王五', email: 'wangwu@example.com' }
                },
                '/api/products/category/electronics': {
                    status: 200,
                    data: {
                        category: '电子产品',
                        message: '这是一个虚拟分类',
                        products: [
                            { id: 1, name: '虚拟产品1 - 电子产品' },
                            { id: 2, name: '虚拟产品2 - 电子产品' }
                        ]
                    }
                },
                '/api/products/v2/123': {
                    status: 200,
                    data: {
                        data: { 
                            id: 123, 
                            name: '示例产品',
                            description: '这是一个示例产品描述' 
                        },
                        meta: { version: 'v2' }
                    }
                }
            };
            
            demoButtons.forEach(button => {
                button.addEventListener('click', () => {
                    const route = button.getAttribute('data-route');
                    const response = mockResponses[route];
                    
                    if (response) {
                        demoOutput.textContent = `请求: ${route}\n状态: ${response.status}\n响应: ${JSON.stringify(response.data, null, 2)}`;
                    } else {
                        demoOutput.textContent = `请求: ${route}\n错误: 未找到该路由的模拟响应`;
                    }
                });
            });
        });
    </script>
</body>
</html>